package com.wjm.api.SynchronizePackage;

/**
 * Data:2022年07月27日
 * Author:wjm
 * Content: 什么是多线程并发安全问题 以及 synchronize关键字(synchronize加在方法上)
 *
 *  案例： 银行取钱操作   账号只有一个
 *        微信，支付宝轮流取钱(多线程并发安全问题写在下面Bank类中)
 *
 *
 *  多线程并发安全问题：
 *        当多个线程并发对同一个临界资源操作时，可能由于线程切换时机的不确定导致线程和设计在执行时出现紊乱
 *        这种紊乱是存在安全性问题的，严重时肯导致程序业务或者系统瘫痪
 *
 *  解决多线程并发安全问题的方法：
 *        就是将异步操作改为同步操作。添加关键字synchronize在方法上就可以将异步改为同步操作
 *        如 public synchronized int getMoney()
 *
 *
 *
 */
public class SynchronizeDemo {
    public static void main(String[] args) {
        //创建银行对象
        Bank bank = new Bank();

        //创建微信支付线程
        Thread weChar = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    int money = bank.getMoney();
                    Thread.yield();
                    System.out.println("微信取出1元当前余额"+money);
                }
            }
        });

        //创建支付宝支付线程
        Thread zfb = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    int money = bank.getMoney();
                    Thread.yield();
                    System.out.println("支付宝取出1元当前余额"+money);
                }
            }
        });

        //启动线程
        weChar.start();
        zfb.start();
    }
}

//创建一个银行类
class Bank{
    private int money = 20;

    //取钱的方法
    public int getMoney(){
        if (money==0){
            throw new RuntimeException("账号余额不足");
        }else {
            /*
            * 存在多线程并发安全问题
            * 当余额只剩1元的时候，轮到微信取钱，微信线程进入到getMoney方法里，因为money=1，所以进入else里面，
            * 而这时候微信线程运行到yield方法被迫让出时间片，而cpu重新分发时间片，而时间片刚好分发给支付宝线程，
            * 因为当前的money=1，所以支付宝也进入了getMoney方法的else里面，接着支付宝线程也执行了yield方法，
            * 让出时间片，然后无论是哪个线程拿到时间片都会进行取钱操作，假设这次还是支付宝拿到时间片，支付宝线程
            * 进行取钱操作，此时的money=0，但是微信线程此时已经在else方法体里， 并且也运行完了yield方法，所以
            * 接下来微信线程会接着进行取钱操作，取完钱之后，money=-1，这时money用于不可能等于0，if语句不会生效
            * ，接下来就会进入死循环
            *
            * */
            Thread.yield();
            return --money;
        }
    }
}